
#region Imports

using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.Text;

using Justa.Reflection;

#endregion

namespace Justa.Data
{
    /// <summary>
    /// 
    /// </summary>
    /// 
    public class EntityManager<T>
    {
        /// <summary>
        /// 
        /// </summary>
        /// 
        private Dictionary<string, bool> cache = new Dictionary<string, bool>();

        /// <summary>
        /// 
        /// </summary>
        /// 
        private DataAccess<T> dataAccess;

        /// <summary>
        /// 
        /// </summary>
        /// 
        public EntityManager(string dataBase) 
        {
            dataAccess = new DataAccess<T>(dataBase);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="entity"></param>
        /// 
        /// <returns></returns>
        /// 
        public T Save(T entity)
        {
            bool required = RequiresCallback<EntityCallbackAttribute>(entity);
            EntityCallbackAttribute callback = null;

            if (required)
            {
                callback = AttributeReflector.GetSingleAttribute<EntityCallbackAttribute>(entity.GetType());
                InvokeMethod<BeforeSaveAttribute>(callback.CallbackType, entity);
            }

            InvokeMethod<BeforeSaveAttribute>(entity);
            T result = default(T); // dataAccess.Insert(entity);

            if (required)
            {
                InvokeMethod<AfterSaveAttribute>(callback.CallbackType, entity);
            }

            InvokeMethod<AfterSaveAttribute>(entity);
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="entity"></param>
        /// 
        [Conditional(Constant.AspectBased)]
        private void InvokeMethod<A>(T entity) where A : Attribute
        {
            Console.WriteLine("invoking dynamic method...");
            List<MethodInfo> methods = AttributeReflector.GetAnotatedMethods<A>(entity.GetType());

            foreach (MethodInfo method in methods)
            {
                method.Invoke(entity, new object[] { });
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="A"></typeparam>
        /// 
        /// <param name="entity"></param>
        /// <param name="actual"></param>
        /// 
        [Conditional(Constant.AspectBased)]
        private void InvokeMethod<A>(Type entity, T actual) where A : Attribute
        {
            object candidate = Activator.CreateInstance(entity);
            List<MethodInfo> methods = AttributeReflector.GetAnotatedMethods<A>(candidate.GetType());
            
            foreach (MethodInfo method in methods)
            {
                Console.WriteLine(method.Name);
                method.Invoke(candidate, new object[] { actual });
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="A"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="entity"></param>
        /// 
        /// <returns></returns>
        /// 
        public bool RequiresCallback<A>(T entity) where A : Attribute
        {
            string name = entity.GetType().Name;
            bool required;

            if (cache.ContainsKey(name))
            {
                cache.TryGetValue(name, out required);
            }
            else
            {
                required = AttributeReflector.IsAnotated<A>(entity.GetType());
                cache.Add(name, required);
            }

            return required;
        }

        //public EntityDefinition GetEntityDefinition(Type entityType)
        //{
        //    EntityDefinition entityDefinition = service.Fetch(entityType.ToString()) as EntityDefinition;

        //    if (entityDefinition == null)
        //    {
        //        entityDefinition = EntityReflector.GetEntityDefinition(entityType);
        //        service.Add(entityType.ToString(), entityDefinition);
        //        return entityDefinition;
        //    }

        //    return entityDefinition;
        //}

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="primaryKeyColumn"></param>
        /// 
        /// <returns></returns>
        /// 
        public long GeneratePrimaryKey(string primaryKeyColumn)
        {
            long pk = 0; return pk;
            //try
            //{
            //    string query = string.Format("select * from GENERATOR_TABLE where primary_key_column = '{0}'", primaryKeyColumn);
            //    // logger.Info("primary key generator query: " + query);

            //    using (IDataReader reader = this.GetQueryDataReader(query))
            //    {
            //        while (reader.Read())
            //        {
            //            pk = Convert.ToInt64(reader["value_column"]);
            //        }
            //    }

            //    pk++;

            //    this.ExecuteNonQuery(string.Format("update GENERATOR_TABLE set value_column = {0} where primary_key_column = '{1}'", pk, primaryKeyColumn));
            //    return pk;
            //}
            //catch (Exception exception)
            //{
            //    throw new DataAccessException("Failed to generate next primary key.", exception);
            //}
        }

    }
}
